/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.server.virtual.model;

import java.util.Calendar;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jcr.RepositoryException;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.stream.StreamSource;
import offset.nodes.server.binary.controller.BinaryAttribute;
import offset.nodes.server.virtual.model.VirtualModel.RepositoryResolver;

public class StyleSheetCache {

    static StyleSheetCache instance = null;

    /**
     * Get cache instance, the singleton pattern
     * 
     * @return
     */
    public static synchronized StyleSheetCache getInstance() {
        if (instance == null)
            instance = new StyleSheetCache();

        return instance;
    }
    HashMap<String, TemplatesInfo> cache = new HashMap<String, TemplatesInfo>();

    /**
     * Get a templates object from the cache. If not present, create it, add it to the cache and return it.
     * 
     * @param model The PageModel
     * @param styleSheetPath The absolute repository path to the style sheet
     * @return The templates object
     * @throws javax.jcr.RepositoryException
     */
    public Templates getTemplates(VirtualModel model, String styleSheetPath) throws RepositoryException {
        TemplatesInfo info = cache.get(styleSheetPath);
        Calendar modification = model.getFileModificationTime(styleSheetPath);
        if (modification == null)
            return null;
        if (info == null || modification.after(info.getModification()))
            synchronized (cache) {
                try {
                    info = new TemplatesInfo(modification, createTemplates(model, styleSheetPath));
                    cache.put(styleSheetPath, info);
                } catch (TransformerConfigurationException ex) {
                    Logger.getLogger(StyleSheetCache.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

        return info.getTemplates();
    }

    /**
     * Invalidate the cache for the given template. The next call to getTemplates will create a fresh templates object.
     * 
     * @param styleSheetPath The absolute repository path to the style sheet node.
     */
    public void invalidateTemplate(String styleSheetPath) {
        cache.remove(styleSheetPath);
    }

    protected Templates createTemplates(VirtualModel model, String styleSheetPath) throws RepositoryException, TransformerConfigurationException {
        BinaryAttribute info = model.getFile(styleSheetPath);
        if (info == null)
            return null;
        Source templateSource = new StreamSource(info.getInput());
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        SAXTransformerFactory saxFactory = (SAXTransformerFactory) transformerFactory;
        saxFactory.setURIResolver(new RepositoryResolver(model));
        Templates result = transformerFactory.newTemplates(templateSource);

        return result;
    }

    class TemplatesInfo {

        Calendar modification;
        Templates templates;

        public TemplatesInfo(Calendar modification, Templates templates) {
            super();
            this.modification = modification;
            this.templates = templates;
        }

        public Calendar getModification() {
            return modification;
        }

        public Templates getTemplates() {
            return templates;
        }
    }
}
